package com.chenju.invokechainservice.utils;

import com.chenju.invokechainservice.common.enums.EasyCode;
import com.chenju.invokechainservice.common.exceptions.FunnyFileNotFoundException;
import com.chenju.invokechainservice.common.exceptions.FunnyValidationException;
import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOExceptionList;
import org.springframework.util.CollectionUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.yaml.snakeyaml.Yaml;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

public class FunnyFileUtils {
    private final static List<String> propertiesFileList = new ArrayList<>();
    static {
        propertiesFileList.add("application.yml");
        propertiesFileList.add("application.yaml");
        propertiesFileList.add("application.properties");
        propertiesFileList.add("bootstrap.yml");
        propertiesFileList.add("bootstrap.yaml");
        propertiesFileList.add("bootstrap.properties");
    }

    public static boolean isPropertiesFile(String fileName){
        return propertiesFileList.contains(fileName);
    }

    public static Properties readPropertiesFile(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            Properties properties = new Properties();
            properties.load(fileInputStream);
            return properties;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Map<String, Object> readYamlFile(File file) {
        try (FileInputStream inputStream = new FileInputStream(file)) {
            Yaml yaml = new Yaml();
            Map<String, Object> data = yaml.load(inputStream);
            return data;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String readYamlPathValue(Map<String, Object> map, String key){
        String[] paths = key.split("\\.");
        for (int i = 0; i < paths.length - 1; i++) {
            map = (Map<String, Object>) map.get(paths[i]);
        }
        if (map != null){
            Object obj = map.get(paths[paths.length - 1]);
            if (obj != null){
                return obj.toString();
            }
        }
        return null;
    }

    public static String getApplicationName(String classesPath) throws FunnyFileNotFoundException {
        File dir = new File(classesPath);
        if (!dir.isDirectory()){
            throw new FunnyFileNotFoundException(String.format("文件 %s 不存在或者不是一个目录", classesPath));
        }
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.isFile() && isPropertiesFile(pathname.getName())) {
                    return true;
                }
                return false;
            }
        });
        if (files == null){
            throw new FunnyFileNotFoundException(String.format("目录 %s 中未找到配置文件相关", classesPath));
        }
        for (File file : files){
            // 先遍历yml文件
            // application.yml || application.yaml
            if ("application.yml".equals(file.getName()) || "application.yaml".equals(file.getName())) {
                Map<String, Object> ymlMap = readYamlFile(file);
                if (ymlMap != null){
                    // 看下是否有active
                    String activeEnv = readYamlPathValue(ymlMap, "spring.profiles.active");
                    if (activeEnv == null) {
                        String applicationName = readYamlPathValue(ymlMap, "spring.application.name");
                        if (applicationName != null){
                            return applicationName;
                        }
                    } else {
                        Map<String, Object> activeYmlMap = readYamlFile(new File(file.getName().replace(".yml", "-" + activeEnv.toString().toLowerCase() + ".yml").replace(".yaml", "-" + activeEnv.toString().toLowerCase() + ".yaml")));
                        if (activeYmlMap != null && readYamlPathValue(activeYmlMap, "spring.application.name") != null) {
                            return readYamlPathValue(activeYmlMap, "spring.application.name");
                        }
                    }
                }
            }
            // 遍历application.properties
            if ("application.properties".equals(file.getName())) {
                Properties properties = readPropertiesFile(file);
                if (properties != null) {
                    // 同样检查是否有active
                    String activeEnv = properties.getProperty("spring.profiles.active");
                    if (activeEnv == null) {
                        String applicationName = properties.getProperty("spring.application.name");
                        if (applicationName != null){
                            return applicationName;
                        }
                    } else {
                        Properties activeProperties = readPropertiesFile(new File(file.getName().replace(".properties", "-" + activeEnv.toLowerCase() + ".properties")));
                        if (activeProperties != null && activeProperties.getProperty("spring.application.name") != null) {
                            return activeProperties.getProperty("spring.application.name");
                        }
                    }
                }

            }
            // 遍历bootstrap.yml
            // bootstrap.yml || bootstrap.yaml
            if ("bootstrap.yml".equals(file.getName()) || "bootstrap.yaml".equals(file.getName())) {
                Map<String, Object> ymlMap = readYamlFile(file);
                if (ymlMap != null && readYamlPathValue(ymlMap, "spring.application.name") != null){
                    return readYamlPathValue(ymlMap, "spring.application.name");
                }
            }
            // 遍历bootstrap.properties
            if ("bootstrap.properties".equals(file.getName())) {
                Properties properties = readPropertiesFile(file);
                if (properties != null){
                    return properties.getProperty("spring.application.name");
                }
            }
        }
        return null;
    }

    public static List<File> listFiles(File directory) {
        List<File> fileList = new ArrayList<>();
        if (directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile()) {
                        fileList.add(file);
                    } else if (file.isDirectory()) {
                        fileList.addAll(listFiles(file));
                    }
                }
            }
        }
        return fileList;
    }

    public static List<File> listFiles(File directory, FileFilter filter) {
        List<File> fileList = new ArrayList<>();
        if (directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile() && filter.accept(file)) {
                        fileList.add(file);
                    } else if (file.isDirectory()) {
                        fileList.addAll(listFiles(file, filter));
                    }
                }
            }
        }

        return fileList;
    }

    public static List<String> getSubModules(String filepath) {
        // 当前只考虑gradle项目且gradle项目根目录中settings.gradle文件中包含子模块及子模块的子模块
        // 对于gradle项目,根目录的settings.gradle文件不包含子模块的子模块,子模块的子模块位于子模块的settings.gradle文件中 -> 暂不处理
        // 对于maven项目, 只考虑根目录中包含的子模块,也就是只有一级子模块
        // 对于maven项目, 存在子模块的子模块的 -> 暂不处理
        if ("".equals(filepath) || filepath == null){
            return new ArrayList<>();
        }

        File buildFile = new File(filepath);
        if (!buildFile.isFile()){
            return new ArrayList<>();
        }

        if ("pom.xml".equals(buildFile.getName().toLowerCase())){
            // maven项目
            return findXMLValueByXPath(filepath, "/project/modules/module");
        }
        if ("settings.gradle".equals(buildFile.getName().toLowerCase())){
            // gradle项目
            return getGradleModules(buildFile);
        }

        return new ArrayList<>();
    }

    public static List<String> getGradleModules(File file) throws FunnyValidationException {
        List<String> modules = new ArrayList<>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            while ((line = reader.readLine()) != null){
                if (line.contains("include")){
                    String moduleString = line.split("include ")[1];
                    if (moduleString.contains(",")){
                        // List<String> collect = Arrays.stream(moduleString.replace("'", "").split(",")).map(String::trim).collect(Collectors.toList());
                        modules.addAll(Arrays.stream(moduleString.replace("'", "").split(",")).map(String::trim).collect(Collectors.toList()));
                    } else {
                        modules.add(moduleString.trim());
                    }
                }
            }
            reader.close();
        } catch (IOException e){
            throw new  FunnyValidationException(String.format("文件【%S】路径或者内错误,请假查", file.getAbsolutePath()));
        }
        return modules.stream().map(module -> module.replace("'","")).collect(Collectors.toList());
    }

    public static String getBuildFilePath(String codePath, String buildFileName) {
        return FunnyStringUtils.joinPath(codePath, buildFileName);
    }

    public static Document loadXMLDocument(String XMLPath) throws FunnyValidationException {
        try {
            // 指定XML文件路径
            File xmlFile = new File(XMLPath);

            // 创建一个DocumentBuilderFactory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // 创建一个DocumentBuilder
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 使用DocumentBuilder解析XML文件，得到Document对象
            return builder.parse(xmlFile);
        } catch (ParserConfigurationException | IOException | SAXException e) {
            throw new FunnyValidationException(String.format("请检查XMLPath %s 是否正确，否则可能是XML格式有误导致解析出现了异常，请检查！", XMLPath));
        }
    }

    public static List<String> findXMLValueByXPath(String XMLPath, String xPathExpression) throws FunnyValidationException{
        // 创建一个list对象来存储查找的节点
        List<String> results = new ArrayList<>();

        // 创建一个XPath对象
        XPathFactory xPathFactory = XPathFactory.newInstance();
        XPath xPath = xPathFactory.newXPath();

        XPathExpression expression = null;
        try {
            expression = xPath.compile(xPathExpression);
            // 加载Document对象
            Document document = loadXMLDocument(XMLPath);
            // 通过XPath表达式获取标签的值
            NodeList nodeList = (NodeList) expression.evaluate(document, XPathConstants.NODESET);

            for (int i=0; i<nodeList.getLength(); i++){
                results.add(nodeList.item(i).getTextContent());
            }

            return results;
        } catch (XPathExpressionException e) {
            throw new FunnyValidationException("XPath Expression不正确，请检查");
        }
    }

    public static List<String> findTargetFiles(String dir, String targetFileName){
        List<String> targetFilePaths = new ArrayList<>();
        File directory = new File(dir);

        if (directory.exists() && directory.isDirectory()) {
            findTargetFilesRecursively(directory, targetFilePaths, targetFileName);
        }

        return targetFilePaths;
    }

    public static void findTargetFilesRecursively(File directory, List<String> targetFilePaths, String targetFileName){
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    findTargetFilesRecursively(file, targetFilePaths, targetFileName);
                } else if (file.getName().equals(targetFileName)) {
                    targetFilePaths.add(file.getAbsolutePath());
                }
            }
        }
    }

    public static List<String> findPOMFiles(String dir){
        return findTargetFiles(dir, "pom.xml");
    }

    public static List<String> findSettingsGradleFiles(String dir){
        return findTargetFiles(dir, "settings.gradle");
    }

    public static List<String> findModulePaths(String dir, String buildTool){
        if ("maven".equals(buildTool)){
            return findMavenModulePaths(dir);
        } else {
            return findGradleModulePaths(dir);
        }
    }

    public static List<String> findMavenModulePaths(String dir){
        List<String> modulePaths = new ArrayList<>();

        List<String> pomFiles = findPOMFiles(dir);

        // 该项目不存在子模块, 直接返回根目录
        if (pomFiles.size() == 1){
            modulePaths.add(dir);
            return modulePaths;
        }

        for (String pomFile : pomFiles){
            List<String> mavenModules = findMavenModules(pomFile);
            if (mavenModules != null){
                for (String mavenModule : mavenModules){
                    if (mavenModule.contains("/")){
                        mavenModule = mavenModule.replace("/", EasyCode.URL_SPLIT);
                    }
                    String modulePath = pomFile.substring(0, pomFile.lastIndexOf(EasyCode.URL_SPLIT)+1) + mavenModule;
                    if (!modulePaths.contains(modulePath)){
                        modulePaths.add(modulePath);
                    }
                }
            }
        }

        return removeNestedPaths(modulePaths);
    }

    public static List<String> findGradleModulePaths(String dir){
        List<String> modulePaths = new ArrayList<>();

        List<String> settingsGradleFiles = findSettingsGradleFiles(dir);

        // 该项目没有settings.gradle文件
        if (settingsGradleFiles.size() == 0){
            modulePaths.add(dir);
            return modulePaths;
        }

        for (String settingsGradleFile : settingsGradleFiles){
            List<String> gradleModules = findGradleModules(settingsGradleFile);

            for (String gradleModule : gradleModules){
                String modulePath = null;
                if (gradleModule.startsWith(":")){
                    gradleModule = gradleModule.substring(1).replace(":", EasyCode.URL_SPLIT);
                    modulePath = FunnyStringUtils.joinPath(dir, gradleModule);
                } else {
                    gradleModule = gradleModule.replace(":", EasyCode.URL_SPLIT);
                    modulePath = FunnyStringUtils.joinPath(settingsGradleFile.substring(0, settingsGradleFile.lastIndexOf(EasyCode.URL_SPLIT)+1), gradleModule);
                }
                if (!modulePaths.contains(modulePath)){
                    modulePaths.add(modulePath);
                }
            }
        }
        return removeNestedPaths(modulePaths);
    }

    public static List<String> findGradleModules(String settingsGradleFile){
        List<String> modules = new ArrayList<>();
        try {
            BufferedReader reader = new BufferedReader(new FileReader(settingsGradleFile));
            String line;
            while ((line = reader.readLine()) != null){
                if (line.startsWith("//") || line.startsWith("#")){
                    continue;
                }
                if (line.contains("include")){
                    String moduleString = line.split("include ")[1];
                    if (moduleString.contains(",")){
                        modules.addAll(Arrays.stream(moduleString.replace("'", "").split(",")).map(String::trim).collect(Collectors.toList()));
                    } else {
                        modules.add(moduleString.replace("'","").trim());
                    }
                }
            }
            reader.close();
        } catch (IOException e){
            throw new  FunnyValidationException(String.format("文件【%S】路径或者内错误,请假查", settingsGradleFile));
        }
        return modules;
    }

    public static List<String> findMavenModules(String pomFile){
        return findXMLValueByXPath(pomFile, "/project/modules/module");
    }

    public static List<String> removeNestedPaths(List<String> paths) {
        List<String> result = new ArrayList<>(paths);

        Iterator<String> iterator = result.iterator();
        while (iterator.hasNext()) {
            String currentPath = iterator.next();

            for (String otherPath : paths) {
                if (!currentPath.equals(otherPath) && otherPath.startsWith(currentPath)) {
                    iterator.remove();
                    break;
                }
            }
        }

        return result;
    }

    public static void main(String[] args) throws IOException {
//        String classPath = "D:\\Project\\testgit\\master\\eureka-server\\target\\classes";
//        String applicationName = getApplicationName(classPath,"");
//        System.out.println(applicationName);
//        String filePath = "D:\\Project\\springboot-easytest\\invokechain-service\\src\\main\\resources\\settings.gradle";
//        List<String> gradleModules = getGradleModules(new File(filePath));
//        System.out.println(gradleModules);

//        String dir = "D:\\Project\\testgit\\mavenproject";
//        List<String> mavenModulePaths = findMavenModulePaths(dir);
//        for (String module : mavenModulePaths){
//            System.out.println(module);
//        }

        String dir = "D:\\Project\\testgit\\project";
        List<String> mavenModulePaths = findGradleModulePaths(dir);
        for (String module : mavenModulePaths){
            System.out.println(module);
        }
    }
}
